package com.ksyun.campus.metaserver.services;

import com.google.gson.Gson;
import com.ksyun.campus.metaserver.domain.ApiResponse;
import com.ksyun.campus.metaserver.domain.FileType;
import com.ksyun.campus.metaserver.domain.ReplicaData;
import com.ksyun.campus.metaserver.domain.StatInfo;
import com.ksyun.campus.metaserver.domain.dto.FileBlockInfo;
import com.ksyun.campus.metaserver.domain.json.DSInstanceInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.beans.Transient;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

@Service
@Slf4j
public class MetaService {

    @Autowired
    private CuratorFramework curatorFramework;

    @Autowired
    private Gson gson;


    public boolean write(String fileSystem, FileBlockInfo fileBlockInfo){
        List<ReplicaData> replicas = fileBlockInfo.getReplicas();
        String path = fileBlockInfo.getPath();
        long length = fileBlockInfo.getLength();
        long offset = fileBlockInfo.getOffset();
        long size = length - offset;

        // 更新文件的元数据信息，例如文件路径、大小、副本信息等
        StatInfo statInfo = new StatInfo();
        LocalDateTime now = LocalDateTime.now();
        Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
        long mtime = instant.toEpochMilli();

        statInfo.setPath(path);
        statInfo.setSize(size);
        statInfo.setMtime(mtime);
        statInfo.setType(FileType.File);
        statInfo.setReplicaData(replicas);

        //将zk结点的文件大小更新
        for (ReplicaData replicaData :replicas) {
            String dsNode = replicaData.getDsNode();

            try {
                byte[] bytes = curatorFramework.getData().forPath("/ds/" + dsNode);
                String dsInstance = new String(bytes);
                DSInstanceInfo dsInstanceInfo = gson.fromJson(dsInstance, DSInstanceInfo.class);
                //拿到上次剩余的容量
                Long useCapacity = dsInstanceInfo.getUseCapacity();
                dsInstanceInfo.setUseCapacity(useCapacity-size);
                String updateDSInfo = gson.toJson(dsInstanceInfo);
                Stat nodeStat = curatorFramework.checkExists().forPath("/ds/"+dsNode);
                if (nodeStat != null) {
                    // 更新节点的值
                    curatorFramework.setData().forPath("/ds/"+dsNode, updateDSInfo.getBytes());

                } else {
                    // 创建节点并设置节点的值
                    curatorFramework.create()
                            .creatingParentsIfNeeded()
                            .forPath("/ds/"+dsNode, updateDSInfo.getBytes());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        String statsInfoJson = gson.toJson(statInfo);


        //保存文件地址
        String zkBackupPath = "/dataStatusBackups"+path;

        try {
            // 检查节点是否已存在
            Stat nodeStat = curatorFramework.checkExists().forPath(zkBackupPath);
            if (nodeStat != null) {
                // 更新节点的值
                curatorFramework.setData().forPath(zkBackupPath, statsInfoJson.getBytes());
            } else {
                // 创建节点并设置节点的值
                curatorFramework.create()
                        .creatingParentsIfNeeded()
                        .forPath(zkBackupPath, statsInfoJson.getBytes());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    // 将文件状态存入zk
    // 在创建文件的时候就将文件要存放的副本确定好
    public ApiResponse createFile(String fileSystem, String path) {
        // 构建文件路径
        String filePath = fileSystem+path;
        // 得到时间
        LocalDateTime now = LocalDateTime.now();
        Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
        long mtime = instant.toEpochMilli();

        StatInfo statInfo=new StatInfo();
        statInfo.setPath(filePath);
        statInfo.setMtime(mtime);
        statInfo.setSize(0);//文件还没有写,文件大小是0
        statInfo.setType(FileType.File);
        // 在这里实现元数据的存储
        // 根据需要定义元数据的数据结构，并将其存储到适当的地方
        List<ReplicaData> replicaDataList=new ArrayList<>();

        List<String> dsNodes;

        try {
            // 拿到/ds目录下的所有子节点
            dsNodes = curatorFramework.getChildren().forPath("/ds");

            // 创建一个Comparator来比较DSInstanceInfo对象的剩余容量(useCapacity)
            Comparator<DSInstanceInfo> capacityComparator = Comparator.comparingLong(DSInstanceInfo::getUseCapacity);

            // 定义一个优先队列(PriorityQueue)来存储剩余容量最多的三个节点
            PriorityQueue<DSInstanceInfo> capacityQueue = new PriorityQueue<>(3, Collections.reverseOrder(capacityComparator));

            // 初始化队列为空
            capacityQueue.clear();

            for (String dsNode : dsNodes) {
                // 拿到结点的数据
                byte[] bytes = curatorFramework.getData().forPath("/ds/" + dsNode);
                String dsInfoJson = new String(bytes);
                DSInstanceInfo dsInstanceInfo = gson.fromJson(dsInfoJson, DSInstanceInfo.class);
                capacityQueue.offer(dsInstanceInfo);
            }

            int count = 0;
            while (count < 3 && !capacityQueue.isEmpty()) {
                DSInstanceInfo dsInstanceInfo = capacityQueue.poll();
                String host = dsInstanceInfo.getHost();
                Integer port = dsInstanceInfo.getPort();
                ReplicaData replicaData = new ReplicaData();
                replicaData.setId(UUID.randomUUID().toString());
                replicaData.setDsNode(host + ":" + port);
                replicaData.setPath(filePath);
                replicaDataList.add(replicaData);
                count++;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }


        statInfo.setReplicaData(replicaDataList);

        /**
         * 将文件存储到zk的对应ds结点中，便于后面对文件的遍历和恢复
         */
        for (ReplicaData replicaData :replicaDataList) {
            String dsNode = replicaData.getDsNode();
            String dsPath = replicaData.getPath();
            String statInfoJson = gson.toJson(statInfo);
            try {
                curatorFramework.create().creatingParentsIfNeeded().forPath("/fileList/"+dsNode+dsPath,
                        statInfoJson.getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        // 将文件状态存到zk中,并且保证可恢复性
        // 定义 ZooKeeper 节点路径用于文件状态备份
        String zkBackupPath = "/dataStatusBackups"+filePath;

        try {
            // 将 StatInfo 对象转换为 JSON 字符串
            String statInfoJson = gson.toJson(statInfo);

            // 在 ZooKeeper 中创建一个节点，将 StatInfo 的 JSON 作为节点数据
            // 若结点已存在，则将其覆盖
            // 检查节点是否已存在
            Stat nodeStat = curatorFramework.checkExists().forPath(zkBackupPath);
            if (nodeStat != null) {
                log.info("该文件结点已存在{}",zkBackupPath);
                return new ApiResponse(500,"文件已存在，请先删除文件",null);
            } else {
                // 节点不存在，创建节点
                String zkNodePath = curatorFramework.create()
                        .creatingParentsIfNeeded()
                        .forPath(zkBackupPath, statInfoJson.getBytes());
                log.info("zkNodePath is " + zkNodePath);
            }
            return new ApiResponse<>(200,"OK",statInfoJson);
        } catch (Exception e) {
            e.printStackTrace();
            return new ApiResponse(500,"fail",null);
        }
    }

    public String getFileStatus(String fileSystem, String path) {
        String filePath=fileSystem+path;

        String zkBackupPath = "/dataStatusBackups"+filePath;
        try {
            byte[] bytes = curatorFramework.getData().forPath(zkBackupPath);
            String statusJson = new String(bytes);
            return statusJson;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public ApiResponse open(String fileSystem, String path) {
//        根据文件path查询三副本的位置，返回客户端具体ds
        String fileStatus = this.getFileStatus(fileSystem, path);
        StatInfo statInfo = gson.fromJson(fileStatus, StatInfo.class);
        List<ReplicaData> replicaData = statInfo.getReplicaData();
        String replicaJsonData = gson.toJson(replicaData);
        return new ApiResponse(200,"OK",replicaJsonData);
    }

    public ApiResponse mkdir(String fileSystem, String path) {
        // 构建文件路径
        String filePath = fileSystem+path;
        // 得到时间
        LocalDateTime now = LocalDateTime.now();
        Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
        long mtime = instant.toEpochMilli();

        StatInfo statInfo=new StatInfo();
        statInfo.setPath(filePath);
        statInfo.setMtime(mtime);
        statInfo.setSize(0);//文件还没有写,文件大小是0
        statInfo.setType(FileType.Directory);

        String statJson = gson.toJson(statInfo);
        try {
            Stat stat = curatorFramework.checkExists().forPath("/dataStatusBackups"+filePath);
            if (stat!=null){
                return new ApiResponse(500,"目录已存在",null);
            }
            curatorFramework.create().creatingParentsIfNeeded().forPath("/dataStatusBackups"+filePath,statJson.getBytes());
            return new ApiResponse(200,"目录创建成功",null);
        } catch (Exception e) {
            e.printStackTrace();
            return new ApiResponse(501,"目录创建失败",null);
        }
    }

    public ApiResponse delete(String fileSystem, String path) {
        String filePath=fileSystem+path;
        try {
            // 检查节点是否存在
            Stat stat = curatorFramework.checkExists().forPath("/dataStatusBackups" + filePath);

            if (stat != null) {
                // 获取节点数据
                byte[] bytes = curatorFramework.getData().forPath("/dataStatusBackups" + filePath);
                String statusJson = new String(bytes);

                // 解析节点数据为StatInfo对象
                StatInfo statInfo = gson.fromJson(statusJson, StatInfo.class);
                List<ReplicaData> replicaDataList = statInfo.getReplicaData();
                long size = statInfo.getSize();

                // 遍历相关数据
                for (ReplicaData replicaData :replicaDataList) {
                    String dsNode = replicaData.getDsNode();

                    // 获取ds节点数据
                    byte[] dsbytes = curatorFramework.getData().forPath("/ds/" + dsNode);
                    String dsInstanceJson = new String(dsbytes);

                    // 解析ds节点数据为DSInstanceInfo对象
                    DSInstanceInfo dsInstanceInfo = gson.fromJson(dsInstanceJson, DSInstanceInfo.class);

                    // 更新使用容量
                    Long useCapacity = dsInstanceInfo.getUseCapacity();
                    //还原空间,其他的不变
                    dsInstanceInfo.setUseCapacity(useCapacity+size);

                    // 更新ds节点的值
                    String updateDSInfo = gson.toJson(dsInstanceInfo);
                    Stat nodeStat = curatorFramework.checkExists().forPath("/ds/"+dsNode);
                    if (nodeStat != null) {
                        // 更新节点的值
                        curatorFramework.setData().forPath("/ds/" + dsNode, updateDSInfo.getBytes());
                    }

                    // 删除ds中的文件
                    try {
                        curatorFramework.delete().forPath("/fileList/"+dsNode+filePath);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                curatorFramework.delete().forPath("/dataStatusBackups" + filePath);
                String replicaDataJsonList = gson.toJson(replicaDataList);
                return new ApiResponse(200, "Node deleted successfully.",replicaDataJsonList);
            } else {
                return new ApiResponse(500, "Node does not exist.",null);
            }
        } catch (Exception e) {
            return new ApiResponse(500, "Failed to delete node: " + e.getMessage(),null);
        }
    }

    public ApiResponse listdir(String fileSystem, String path) {
        String filePath=fileSystem+path;

        List<String> statusInfoList=new ArrayList<>();
        //获得filePath结点下的所有子节点
        try {
            List<String> fileNodes = curatorFramework.getChildren().forPath("/dataStatusBackups" + filePath);
            for (String fileNode :fileNodes) {
                //获取结点的Stats
                byte[] bytes = curatorFramework.getData().forPath("/dataStatusBackups" + filePath + "/" + fileNode);
                String statsJson = new String(bytes);
                System.out.println(statsJson);
                statusInfoList.add(statsJson);
            }
            return new ApiResponse(200,"OK",statusInfoList);
        } catch (Exception e) {
            e.printStackTrace();
            return new ApiResponse(500,"fail",null);
        }
    }
}
